/*
 *
 * Copyright (c) 2020 The Raptor Authors. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

#ifndef RAPTOR_LITE_UTILS_SLICE_H_
#define RAPTOR_LITE_UTILS_SLICE_H_

#include <stddef.h>
#include <stdint.h>
#include <string>
#include <ostream>

namespace raptor {
class SliceRefCount;

class Slice final {
public:
    Slice();
    Slice(const char *ptr); // size = strlen(ptr)
    Slice(const void *buf, size_t len);
    Slice(void *buf, size_t len);
    Slice(const char *ptr, size_t len);

    Slice(const std::string &str)
        : Slice(str.data(), str.size()) {}

    ~Slice();

    Slice(const Slice &oth);
    Slice &operator=(const Slice &oth);

    Slice(Slice &&oth);
    Slice &operator=(Slice &&oth);

    size_t size() const;
    const char *begin() const;
    const char *end() const;

    size_t length() const { return size(); }
    bool empty() const { return (size() == 0); }
    const char *data() const { return begin(); }
    char *buffer() const { return const_cast<char *>(begin()); }

    void RemoveHead(size_t bytes);
    void RemoveTail(size_t bytes);

    bool compare(const std::string &s) const { return compare(s.data(), s.size()); }
    bool operator==(const Slice &s) const { return compare(s.data(), s.size()); }
    bool operator!=(const Slice &s) const { return !compare(s.data(), s.size()); }

    Slice &operator+=(const Slice &s);
    void clear();

private:
    bool compare(const char *ptr, size_t len) const;

    enum : int { SLICE_INLINED_SIZE = 23 };

    SliceRefCount *refs_;
    union slice_data {
        struct {
            size_t length;
            char *bytes;
        } refcounted;
        struct {
            uint8_t length;
            char bytes[SLICE_INLINED_SIZE];
        } inlined;
    } data_;

    friend class SliceBuffer;
    friend Slice MakeSliceByLength(size_t len);
};

Slice MakeSliceByLength(size_t len);

// Merge two slice, s1 in front and s2 in the back
Slice operator+(Slice s1, Slice s2);

static inline std::string to_string(const Slice &s) { return std::string(s.data(), s.size()); }
static inline std::ostream &operator<<(std::ostream &os, const Slice &s) {
    os << std::string(s.data(), s.size());
    return os;
}
} // namespace raptor

#endif // RAPTOR_LITE_UTILS_SLICE_H_
